package main

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
)

func GoID() int64 {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	// 得到id字符串
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, err := strconv.Atoi(idField)
	if err != nil {
		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
	}
	return int64(id)
}

type RecursiveMutex struct {
	sync.Mutex
	owner     int64 // 当前持有锁的 goroutine id
	recursion int32 // 这个 goroutine 重入的次数
}

func (m *RecursiveMutex) Lock() {
	gid := GoID()
	// 如果当前锁是当前协程
	if atomic.LoadInt64(&m.owner) == gid {
		m.recursion++ // 这里可以直接 ++，因为当前是重入状态，已经持有了锁
		return
	}
	m.Mutex.Lock()                   // 加锁
	atomic.StoreInt64(&m.owner, gid) // 设置 owner
	m.recursion = 1                  // 被重入 1 次
}

func (m *RecursiveMutex) Unlock() {
	gid := GoID()
	// 如果是当前协程，才能解锁
	if atomic.LoadInt64(&m.owner) != gid {
		// 非当前协程，直接退出，不给解锁
		return
	}
	// 已经在锁内了，准备解锁
	m.recursion--
	if m.recursion == 0 { // 重入次数为 0，则 Unlock
		atomic.StoreInt64(&m.owner, -1) // 解锁前，将 owner 置 -1
		m.Mutex.Unlock()
	}
}

func main() {

}
